package com.neusoft.neuiotms.dcam.common.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityNotFoundException;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.neusoft.bizcore.web.bean.TreeBean;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.neuiotms.dcam.common.converter.LocationConverter;
import com.neusoft.neuiotms.dcam.common.dto.LocationDTO;
import com.neusoft.neuiotms.dcam.common.model.Location;
import com.neusoft.neuiotms.dcam.common.repository.LocationRepository;
import com.neusoft.neuiotms.dcam.common.service.LocationService;
import com.neusoft.neuiotms.dcam.fixed.dto.AssetDTO;
import com.neusoft.neuiotms.dcam.fixed.service.AssetService;

@Service
public class LocationServiceImpl implements LocationService {

    @Autowired
    private LocationRepository locationRepository;
    @Autowired
    private LocationConverter locationConverter;
    @Autowired
    private AssetService assetService;

    @Override
    public List<LocationDTO> findAll() {
        final List<Location> models = this.locationRepository.findAll();
        final List<LocationDTO> dtos = this.locationConverter.toListDTO(models);
        return dtos;
    }

    @Override
    public Page<LocationDTO> search(final Pageable pageable, final Searchable searchable) {
        final Page<Location> models = this.locationRepository.search(pageable, searchable);
        return this.locationConverter.toPageDTO(models);
    }

    @Override
    public LocationDTO create(final LocationDTO dto) {

        // 校验编码重复性
        final Location check = this.locationRepository.findByCode(dto.getCode());
        if (check != null) {
            throw new UnitedException("位置编码不能重复", new Object[] {});
        }

        final Location model = this.locationConverter.toModel(dto);

        if (!StringUtils.isEmpty(dto.getParentCode())) {
            try {
                final Location pModel = this.locationRepository.findByCode(dto.getParentCode());
                model.setParent(pModel);
            } catch (final EntityNotFoundException e) {
                throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {dto.getCode() });
            }
        }

        this.locationRepository.save(model);
        return this.locationConverter.toDTO(model);
    }

    @Override
    public LocationDTO details(final Long id) {
        final Location model;
        try {
            model = this.locationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.locationConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        final Location model;
        try {
            model = this.locationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        final Searchable searchable = new Searchable();
        searchable.put("locationId", id);
        final Page<AssetDTO> assets = this.assetService.search(Pageable.unpaged(), searchable);
        if (assets.getTotalElements() > 0) {
            throw new UnitedException("此位置节点已有资产，请先删除资产", new Object[] {id });
        }

        final List<Location> models = this.locationRepository.findByParentId(model.getId());

        if ((models != null) && (models.size() > 0)) {
            throw new UnitedException("此位置节点包含其他位置节点,请先删除子节点", new Object[] {id });
        } else {
            try {
                this.locationRepository.deleteById(id);
            } catch (final Exception e) {
                throw new UnitedException("业务数据请勿删除", new Object[] {id });
            }
        }
    }

    @Override
    public LocationDTO update(final Long id, final LocationDTO dto) {

        // 校验编码重复性
        final Location check = this.locationRepository.findByCode(dto.getCode());
        if ((check != null) && (check.getId() != id)) {
            throw new UnitedException("位置编码不能重复", new Object[] {});
        }

        // 校验父节点不能为本身
        if (dto.getCode().equals(dto.getParentCode())) {
            throw new UnitedException("不能指定自己为父节点", new Object[] {});
        }

        Location model;
        try {
            model = this.locationRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());
        model = this.locationConverter.toModel(dto);

        if (!StringUtils.isEmpty(dto.getParentCode())) {
            try {
                final Location pModel = this.locationRepository.findByCode(dto.getParentCode());
                model.setParent(pModel);
            } catch (final EntityNotFoundException e) {
                throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {dto.getParentCode() });
            }
        } else if ((dto.getParentId() != null) && (dto.getParentId() != -1)) {
            try {
                final Location pModel = this.locationRepository.getOne(dto.getParentId());
                model.setParent(pModel);
            } catch (final EntityNotFoundException e) {
                throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {dto.getParentId() });
            }
        } else {
            model.setParent(null);
        }

        this.locationRepository.save(model);

        return this.locationConverter.toDTO(model);
    }

    @Override
    public List<TreeBean> locationTree() {

        final List<Location> rootModel = this.locationRepository.findByParentId(null);

        final List<TreeBean> rootBean = new ArrayList<>();

        rootModel.stream().map(m -> {
            final TreeBean o = new TreeBean();
            o.setCode(m.getCode() + "");
            o.setTitle(m.getName());
            final Map<String, Object> attr = new HashMap<>();
            attr.put("id", m.getId());
            attr.put("parentId", m.getParent() == null ? "-1" : m.getParent().getId());
            attr.put("sort", m.getSort());
            o.setAttr(attr);
            this.subTree(o);
            return o;
        }).forEach(rootBean::add);

        return rootBean;
    }

    private void subTree(final TreeBean parentBean) {
        final List<Location> childrenModels =
                this.locationRepository.findByParentCode(parentBean.getCode());

        final List<TreeBean> children = new ArrayList<>();
        childrenModels.stream().map(o -> {
            final TreeBean t = new TreeBean();
            t.setCode(o.getCode());
            t.setTitle(o.getName());
            final Map<String, Object> attr = new HashMap<>();
            attr.put("id", o.getId());
            attr.put("parentId", o.getParent() == null ? "-1" : o.getParent().getId());
            attr.put("sort", o.getSort());
            t.setAttr(attr);
            return t;
        }).forEach(children::add);

        if (children.size() > 0) {
            parentBean.setChildren(children);

            children.forEach(s -> {
                this.subTree(s);
            });
        } else {
            parentBean.setChildren(new ArrayList<TreeBean>());
        }
    }
}
